package ynu.edu.merchant.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.crypto.digest.BCrypt;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ynu.edu.common.exception.BusinessException;
import ynu.edu.merchant.entity.Merchant;
import ynu.edu.merchant.entity.MerchantCategory;
import ynu.edu.merchant.dto.MerchantDTO;
import ynu.edu.merchant.dto.MerchantLoginDTO;
import ynu.edu.merchant.repository.MerchantCategoryRepository;
import ynu.edu.merchant.repository.MerchantRepository;
import ynu.edu.merchant.service.MerchantService;
import ynu.edu.merchant.util.JwtUtil;
import ynu.edu.merchant.vo.MerchantVO;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 商家服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MerchantServiceImpl implements MerchantService {

    private final MerchantRepository merchantRepository;
    private final MerchantCategoryRepository categoryRepository;
    private final JwtUtil jwtUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MerchantVO createMerchant(MerchantDTO merchantDTO) {
        // 检查用户名是否已存在
        if (merchantRepository.existsByUsername(merchantDTO.getUsername())) {
            throw new BusinessException("用户名已存在");
        }
        
        // 检查分类是否存在
        if (merchantDTO.getCategoryId() != null) {
            categoryRepository.findById(merchantDTO.getCategoryId())
                    .orElseThrow(() -> new BusinessException("商家分类不存在"));
        }

        // 创建商家实体
        Merchant merchant = new Merchant();
        BeanUtil.copyProperties(merchantDTO, merchant, "id", "password");
        
        // 加密密码
        merchant.setPassword(BCrypt.hashpw(merchantDTO.getPassword()));

        // 保存商家
        merchant = merchantRepository.save(merchant);

        // 返回商家信息
        return convertToVO(merchant);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MerchantVO updateMerchant(MerchantDTO merchantDTO) {
        // 检查商家是否存在
        Merchant merchant = merchantRepository.findById(merchantDTO.getId())
                .orElseThrow(() -> new BusinessException("商家不存在"));

        // 检查分类是否存在
        if (merchantDTO.getCategoryId() != null) {
            categoryRepository.findById(merchantDTO.getCategoryId())
                    .orElseThrow(() -> new BusinessException("商家分类不存在"));
        }

        // 更新商家信息
        BeanUtil.copyProperties(merchantDTO, merchant, "id", "username", "password", "createTime", "updateTime", "isDeleted");

        // 保存商家
        merchant = merchantRepository.save(merchant);

        // 返回商家信息
        return convertToVO(merchant);
    }

    @Override
    public MerchantVO getMerchantById(Long id) {
        Merchant merchant = merchantRepository.findById(id)
                .orElseThrow(() -> new BusinessException("商家不存在"));
        return convertToVO(merchant);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteMerchant(Long id) {
        Merchant merchant = merchantRepository.findById(id)
                .orElseThrow(() -> new BusinessException("商家不存在"));
        merchant.setIsDeleted(1);
        merchantRepository.save(merchant);
        return true;
    }

    @Override
    public Page<MerchantVO> getMerchantsByCategoryId(Long categoryId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "rating"));
        Page<Merchant> merchantPage = merchantRepository.findByCategoryIdAndStatusAndIsDeleted(
                categoryId, 1, 0, pageable);
        return merchantPage.map(this::convertToVO);
    }

    @Override
    public Page<MerchantVO> searchMerchantsByName(String name, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "rating"));
        Page<Merchant> merchantPage = merchantRepository.findByNameContainingAndStatusAndIsDeleted(
                name, 1, 0, pageable);
        return merchantPage.map(this::convertToVO);
    }

    @Override
    public List<MerchantVO> getRecommendedMerchants() {
        List<Merchant> merchants = merchantRepository.findByStatusAndIsDeletedOrderByRatingDesc(1, 0);
        return merchants.stream()
                .limit(10)
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateMerchantStatus(Long id, Integer status) {
        Merchant merchant = merchantRepository.findById(id)
                .orElseThrow(() -> new BusinessException("商家不存在"));
        merchant.setStatus(status);
        merchantRepository.save(merchant);
        return true;
    }
    
    @Override
    public String login(MerchantLoginDTO loginDTO) {
        // 根据用户名查询商家
        Merchant merchant = merchantRepository.findByUsername(loginDTO.getUsername())
                .orElseThrow(() -> new BusinessException("用户名或密码错误"));

        // 验证密码
        if (!BCrypt.checkpw(loginDTO.getPassword(), merchant.getPassword())) {
            throw new BusinessException("用户名或密码错误");
        }

        // 检查商家状态
        if (merchant.getStatus() == 0) {
            throw new BusinessException("账号已被禁用");
        }

        // 生成JWT令牌
        return jwtUtil.generateToken(merchant.getUsername(), merchant.getId());
    }

    /**
     * 将商家实体转换为VO
     *
     * @param merchant 商家实体
     * @return 商家VO
     */
    private MerchantVO convertToVO(Merchant merchant) {
        MerchantVO merchantVO = new MerchantVO();
        BeanUtil.copyProperties(merchant, merchantVO, "password");

        // 设置分类名称
        if (merchant.getCategoryId() != null) {
            categoryRepository.findById(merchant.getCategoryId())
                    .ifPresent(category -> merchantVO.setCategoryName(category.getName()));
        }

        return merchantVO;
    }
} 